package org.hxzon.cyclone;

//core.h
//四元数
public class Quaternion {
    //一个实部，三个虚部
    public double r;
    public double i;
    public double j;
    public double k;

    //public double[] data=new double[4];//

    //============

    //a zero rotation.
    public Quaternion() {
        this(1, 0, 0, 0);
    }

    /**
     * @note The given orientation does not need to be normalised,
     * and can be zero. This function will not alter the given
     * values, or normalise the quaternion. To normalise the
     * quaternion (and make a zero quaternion a legal rotation),
     * use the normalise function.
     *
     * @see normalise
     */
    public Quaternion(double r, double i, double j, double k) {
        this.r = r;
        this.i = i;
        this.j = j;
        this.k = k;
    }

    /**
     * Normalises the quaternion to unit length, making it a valid
     * orientation quaternion.
     */
    public void normalise() {
        double d = r * r + i * i + j * j + k * k;

        // Check for zero length quaternion, and use the no-rotation
        // quaternion in that case.
        if (d < Global.real_epsilon) {
            r = 1;
            return;
        }

        d = ((double) 1.0) / Util.realSqrt(d);
        r *= d;
        i *= d;
        j *= d;
        k *= d;
    }

    //四元数相乘
    public void multiUpdate(Quaternion multiplier) {
        r = r * multiplier.r - i * multiplier.i - j * multiplier.j - k * multiplier.k;
        i = r * multiplier.i + i * multiplier.r + j * multiplier.k - k * multiplier.j;
        j = r * multiplier.j + j * multiplier.r + k * multiplier.i - i * multiplier.k;
        k = r * multiplier.k + k * multiplier.r + i * multiplier.j - j * multiplier.i;
    }

    /**
     * Adds the given vector to this, scaled by the given amount.
     * This is used to update the orientation quaternion by a rotation
     * and time.
     *
     * @param vector The vector to add.
     *
     * @param scale The amount of the vector to add.
     */
    public void addScaledVector(Vector3 vector, double scale) {
        Quaternion q = new Quaternion(0, vector.x * scale, vector.y * scale, vector.z * scale);
        q.multiUpdate(this);
        r += q.r * ((double) 0.5);
        i += q.i * ((double) 0.5);
        j += q.j * ((double) 0.5);
        k += q.k * ((double) 0.5);
    }

    public void rotateByVector(Vector3 vector) {
        Quaternion q = new Quaternion(0, vector.x, vector.y, vector.z);
        this.multiUpdate(q);
    }

    public Quaternion copy() {
        return new Quaternion(r, i, j, k);
    }

}
